package cn.tannn.surenessdemo.sureness.annotation;

import cn.jdevelops.annotation.mapping.PathRestController;
import com.usthe.sureness.matcher.PathTreeProvider;
import com.usthe.sureness.util.ClassScanner;
import com.usthe.sureness.util.SurenessCommonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;

/**
 * use annotation load sureness pathTree config data
 *
 * @author tomsun28
 * @date 23:57 2020-03-16
 */
public class MyAnnotationPathTreeProvider implements PathTreeProvider {

    private static final Logger logger = LoggerFactory.getLogger(MyAnnotationPathTreeProvider.class);

    /**
     * Specified scan package
     */
    private List<String> scanPackages;

    /**
     * Scanned class
     */
    private List<Class<?>> scanClasses;

    /**
     * if is init
     */
    private volatile boolean isInit = false;

    private synchronized void init() {
        if (scanPackages == null) {
            scanPackages = Collections.singletonList(" ");
        }
        isInit = true;
        scanClasses = ClassScanner.scanPackages(scanPackages);
        logger.info("sureness: annotationLoader success init, load {} classes total.", scanClasses.size());
    }

    @Override
    public Set<String> providePathData() {
        if (!isInit) {
            init();
        }
        // 没有配套的拦截注解所以不管了 参见AnnotationPathTreeProvider
        Set<String> resource = new HashSet<>();
        return SurenessCommonUtil.attachContextPath(getContextPath(), resource);
    }

    @Override
    public Set<String> provideExcludedResource() {
        if (!isInit) {
            init();
        }
        // get provide exclude path data from load class
        Set<String> resource = new HashSet<>();
        for (Class<?> clazz : scanClasses) {
            for (Method method : clazz.getDeclaredMethods()) {
                if (method.isAnnotationPresent(ApiMapping.class)) {
                    ApiMapping apiMapping = method.getDeclaredAnnotation(ApiMapping.class);
                    // true 不检查
                    if (!apiMapping.checkToken()) {
                        String[] value = apiMapping.value();
                        RequestMethod[] apiMethods = apiMapping.method();
                        // 特殊清空会出现两个以上接口名和method
                        for (String s : value) {
                            for (RequestMethod apiMethod : apiMethods) {
                                String apiName = getTargetResource(clazz, s);
                                String path = apiName.toLowerCase()
                                        + "===" + apiMethod.name().toLowerCase();
                                resource.add(path);
                                if (logger.isDebugEnabled()) {
                                    logger.debug("sureness: annotationLoader load exclude path {}.", path);
                                }
                            }
                        }
                    }
                }
            }
        }
        return SurenessCommonUtil.attachContextPath(getContextPath(), resource);
    }

    public void setScanPackages(List<String> scanPackages) {
        this.scanPackages = scanPackages;
    }


    private String[] getRequestMappingValues(Class<?> clazz) {
        Annotation[] annotations = clazz.getAnnotations();
        for (Annotation annotation : annotations) {
            if (annotation instanceof RequestMapping) {
                return ((RequestMapping) annotation).value();
            } else if (annotation instanceof PathRestController) {
                return ((PathRestController) annotation).value();
            }
        }
        return new String[0];
    }

    private String requestMappingValue(Class<?> clazz) {
        String[] apiPName  = getRequestMappingValues(clazz);
        // 只能写一个，写多个给我滚
        if (Objects.requireNonNull(apiPName).length>0) {
            if (apiPName.length > 0) {
                return apiPName[0];
            }
        }
        return null;
    }


    private String getTargetResource(Class<?> clazz, String apiMappingValue) {
        String value = requestMappingValue(clazz);
        if (Objects.nonNull(value)) {
            return apiMappingValue.startsWith("/") ? value + apiMappingValue : value + "/" + apiMappingValue;
        }
        return apiMappingValue;
    }

}
